package com.blue.base.demo.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.blue.base.common.constants.UserConstants;
import com.blue.base.common.exception.AuthException;
import com.blue.base.common.exception.BaseException;
import com.blue.base.demo.entity.SysLoginUserInfo;
import com.blue.base.common.jwt.JwtTokenUtil;
import com.blue.base.demo.entity.User;
import com.blue.base.demo.mappers.MenuMapper;
import com.blue.base.demo.mappers.UserMapper;
import com.blue.base.demo.vo.MenuVo;
import com.blue.base.redis.utils.RedisUtils;
import com.blue.base.security.api.AuthService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author liulei
 * @version 1.0
 */
@Slf4j
@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getAccount, account));
        if (null == user) {
            throw new BaseException(600, "用户名/密码错误");
        }
        //TODO 待抽离出一个bean出来
        UserDetails userDetails = new UserDetails() {
            @Override
            public Collection<? extends GrantedAuthority> getAuthorities() {
                Collection<GrantedAuthority> authorities = Lists.newArrayList();
                //为了测试方便，临时赋予用户一个admin角色
                List<String> urls = Arrays.asList("admin");
                for (String url : urls) {
                    authorities.add((GrantedAuthority) () -> url);
                }
                return authorities;
            }

            @Override
            public String getPassword() {
                //为了测试方便，临时赋予用户一个密码
                return new BCryptPasswordEncoder().encode("123456");
            }

            @Override
            public String getUsername() {
                return account;
            }

            @Override
            public boolean isAccountNonExpired() {
                return true;
            }

            @Override
            public boolean isAccountNonLocked() {
                return true;
            }

            @Override
            public boolean isCredentialsNonExpired() {
                return true;
            }

            @Override
            public boolean isEnabled() {
                return true;
            }
        };
        return userDetails;
    }

    @Override
    public String generateTokenByAccount(UserDetails userDetails) {
        SysLoginUserInfo userInfo = new SysLoginUserInfo();
        // 角色
        List<String> authorities = userDetails.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList());
        userInfo.setAuthorities(authorities);
        // 账户
        userInfo.setAccount(userDetails.getUsername());
        try {
            // 存储到token
            String userRedisKey = UserConstants.getUserRedisKey(userInfo.getAccount());
            RedisUtils.set(userRedisKey, JSON.toJSONString(userInfo), UserConstants.USER_INFO_EXPIRED);
            return JwtTokenUtil.generateToken(userDetails.getUsername());
        } catch (Exception e) {
            log.error("Token生成异常", e);
            throw new AuthException(AuthException.Token_Error, "请求异常，请联系管理员");
        }
    }

    /**
     * token校验
     */
    @Override
    public String getAccountByToken(String token) {
        if (StringUtils.isEmpty(token)) {
            log.error("请求格式异常，缺少token");
            throw new AuthException(AuthException.Token_Null, "用户未登录");
        }
        JwtTokenUtil.validateToken(token);
        SysLoginUserInfo userInfo = JwtTokenUtil.getJwtPayLoad(token, SysLoginUserInfo.class);
        return userInfo.getAccount();
    }

    @Override
    public boolean hasRight(List<String> roles, String servletPath) {
        List<MenuVo> permissions = menuMapper.getMenusByRoleNames(roles);
        Optional<MenuVo> any = permissions.stream()
                .filter(menu -> menu.getUrl().equals(servletPath))
                .findAny();
        return any.isPresent();
    }
}
